home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Archives / GNU / GNUPLOTsrc.lha / amiga.c < prev    next >
C/C++ Source or Header  |  1996-01-22  |  10KB  |  341 lines

  1.  
  2. /*
  3.  * amiga.c
  4.  *
  5.  * Written by Carsten Steger <stegerc@informatik.tu-muenchen.de>
  6.  *
  7.  * Popen and pclose have the same semantics as their UNIX counterparts.
  8.  *
  9.  * Additionally, they install an exit trap that closes all open pipes,
  10.  * should the program terminate abnormally.
  11.  */
  12.  
  13.  
  14. #include <stdio.h>
  15. #include <ios1.h>
  16. #include <error.h>
  17. #include <string.h>
  18. #include <stdlib.h>
  19.  
  20. #include <exec/types.h>
  21. #include <dos/dos.h>
  22. #include <dos/dosextens.h>
  23. #include <dos/dostags.h>
  24. #include <proto/exec.h>
  25. #include <proto/dos.h>
  26.  
  27. #ifdef PIPES  /* dont bother if pipes are not being used elsewhere */
  28.  
  29. /* Maximum number of open pipes. If this is set to a number > 10, the code
  30.  * that constructs the pipe names in popen () will have to be modified.
  31.  */
  32. #define MAX_OPEN_PIPES 10
  33.  
  34. /* We need at least this Dos version to work. */
  35. #define DOS_VERSION 37
  36.  
  37.  
  38. /* This data structure is sent to the child process with sm_Cmd set to the
  39.  * command to be executed. When the child is done it sets sm_RetCode to
  40.  * the return code of the executed command.
  41.  */
  42. struct StartupMessage {
  43.   struct Message sm_Msg;
  44.   LONG sm_RetCode;
  45.   UBYTE *sm_Cmd;
  46. };
  47.  
  48. /* We keep track of the open pipe through this data structure. */
  49. struct PipeFileDescriptor {
  50.   FILE *pfd_File;
  51.   struct StartupMessage pfd_Msg;
  52. };
  53.  
  54.  
  55. /* Needed to check for the required Dos version. */
  56. extern struct DosLibrary *DOSBase;
  57.  
  58. /* This data structure keeps track of the pipes that are still open. */
  59. static struct PipeFileDescriptor OpenPipes[MAX_OPEN_PIPES];
  60.  
  61. /* The address of the process that calls popen or pclose. */
  62. static struct Process *ThisProcess;
  63.  
  64. /* Are we called for the first time? */
  65. static LONG FirstCall = TRUE;
  66.  
  67.  
  68. /* Prototypes for the functions below. */
  69. FILE *popen (const char *command, const char *mode);
  70. int pclose (FILE *stream);
  71. static void CleanUpPipes (void);
  72. static int __saveds ChildEntry (void);
  73.  
  74.  
  75. FILE *popen (command, mode)
  76. const char *command;
  77. const char *mode;
  78. {
  79.   UBYTE PipeName[16];
  80.   ULONG ProcAddress;
  81.   UBYTE HexDigit;
  82.   UBYTE *NextChar;
  83.   struct CommandLineInterface *ThisCli;
  84.   struct PipeFileDescriptor *PipeToUse;
  85.   LONG PipeNumToUse;
  86.   LONG ChildPipeMode;
  87.   BPTR ChildPipe;
  88.   FILE *ParentPipe;
  89.   struct Process *ChildProcess;
  90.   struct TagItem NewProcTags[8] = {
  91.     {NP_Entry, (Tag) ChildEntry},
  92.     {NP_Cli, TRUE},
  93.     {NP_StackSize, 4096},
  94.     {NP_Input, NULL},
  95.     {NP_Output, NULL},
  96.     {NP_CloseInput, FALSE},
  97.     {NP_CloseOutput, FALSE},
  98.     {TAG_DONE, 0}
  99.   };
  100.  
  101.   /* Test whether we're using the right Dos version. */
  102.   if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
  103.     errno = EPIPE;
  104.     return NULL;
  105.   }
  106.  
  107.   /* If we're called for the first time, install exit trap and do some
  108.    * initialisation stuff.
  109.    */
  110.   if (FirstCall) {
  111.     /* Initialise pipe file descriptor table. */
  112.     memset (OpenPipes, 0, sizeof (OpenPipes));
  113.     
  114.     /* Install our exit trap. */
  115.     if (atexit (CleanUpPipes) != 0) {
  116.       errno = EPIPE;
  117.       return NULL;
  118.     }
  119.     FirstCall = FALSE;
  120.   }
  121.  
  122.   /* If we don't know our process' address yet, we should get it now. */
  123.   if (ThisProcess == NULL)
  124.     ThisProcess = (struct Process *) FindTask (NULL);
  125.  
  126.   /* Get our Cli structure. */
  127.   ThisCli = Cli ();
  128.  
  129.   /* Now try to find an empty slot in the pipe file descriptor table.
  130.    * Return NULL if no slot is available.
  131.    */
  132.   for (PipeNumToUse = 0; PipeNumToUse < MAX_OPEN_PIPES; PipeNumToUse++)
  133.     if (OpenPipes[PipeNumToUse].pfd_File == NULL) break;
  134.   if (PipeNumToUse >= MAX_OPEN_PIPES) {
  135.     errno = EMFILE;
  136.     return NULL;
  137.   }
  138.   PipeToUse = &OpenPipes[PipeNumToUse];
  139.  
  140.   /* Check if the specified mode is valid. */
  141.   if (strcmp (mode, "r") == 0)
  142.     ChildPipeMode = MODE_NEWFILE;
  143.   else if (strcmp (mode, "w") == 0)
  144.     ChildPipeMode = MODE_OLDFILE;
  145.   else {
  146.     errno = EINVAL;
  147.     return NULL;
  148.   }
  149.  
  150.   /* Make a unique file name for the pipe that we are about to open. The
  151.    * file name has the following format: "PIPE:XXXXXXXX_Y", where
  152.    * XXXXXXXX is the address of our process in hex, Y is the number of the
  153.    * slot in the pipe descriptor table that we will use. The code is
  154.    * equivalent to
  155.    * sprintf (PipeNameWriter, "PIPE:%08lX_%1d", ThisProcess, PipeNumToUse);
  156.    * but it doesn't need sprintf and therefore makes programs that don't
  157.    * use printf a lot shorter.
  158.    */
  159.   strcpy (PipeName, "PIPE:00000000_0");
  160.   NextChar = PipeName + 12;
  161.   ProcAddress = (ULONG) ThisProcess;
  162.   while (ProcAddress != 0) {
  163.     HexDigit = (UBYTE) ProcAddress & 0xf;
  164.     HexDigit = HexDigit < 10 ? HexDigit + '0' : HexDigit - 10 + 'A';
  165.     *NextChar-- = HexDigit;
  166.     ProcAddress >>= 4;
  167.   }
  168.   /* If MAX_OPEN_PIPES > 10, this will have to be modified. */
  169.   PipeName[14] = ((UBYTE) PipeNumToUse) + '0';
  170.  
  171.   /* Create tags for the child process. */
  172.   if (ThisProcess->pr_CLI)
  173.     NewProcTags[2].ti_Data = ThisCli->cli_DefaultStack << 2;
  174.   else
  175.     NewProcTags[2].ti_Data = ThisProcess->pr_StackSize;
  176.  
  177.   /* Open both ends of the pipe. The child's side is opened with Open (),
  178.    * while the parent's side is opened with fopen ().
  179.    */
  180.   ChildPipe = Open (PipeName, ChildPipeMode);
  181.   ParentPipe = fopen (PipeName, mode);
  182.   if (ChildPipeMode == MODE_NEWFILE) {
  183.     NewProcTags[3].ti_Data = Input ();
  184.     NewProcTags[4].ti_Data = ChildPipe;
  185.     NewProcTags[5].ti_Data = FALSE;
  186.     NewProcTags[6].ti_Data = TRUE;
  187.   } else {
  188.     NewProcTags[3].ti_Data = ChildPipe;
  189.     NewProcTags[4].ti_Data = Output ();
  190.     NewProcTags[5].ti_Data = TRUE;
  191.     NewProcTags[6].ti_Data = FALSE;
  192.   }
  193.   if (ChildPipe == NULL || ParentPipe == NULL) {
  194.     errno = EPIPE;
  195.     goto cleanup;
  196.   }
  197.  
  198.   /* Now generate a entry in the pipe file descriptor table. */
  199.   PipeToUse->pfd_Msg.sm_Cmd = malloc (strlen (command) + 1);
  200.   if (PipeToUse->pfd_Msg.sm_Cmd == NULL) {
  201.     errno = ENOMEM;
  202.     goto cleanup;
  203.   }
  204.   strcpy (PipeToUse->pfd_Msg.sm_Cmd, command);
  205.   PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort = CreateMsgPort ();
  206.   if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL) {
  207.     errno = ENOMEM;
  208.     goto cleanup;
  209.   }
  210.   PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
  211.   PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Pri = 0;
  212.   PipeToUse->pfd_Msg.sm_Msg.mn_Length = sizeof (struct StartupMessage);
  213.   PipeToUse->pfd_File = ParentPipe;
  214.  
  215.   /* Now create the child process. */
  216.   ChildProcess = CreateNewProc (NewProcTags);
  217.   if (ChildProcess == NULL) {
  218.     errno = ENOMEM;
  219.     goto cleanup;
  220.   }
  221.  
  222.   /* Pass the startup message to the child process. */
  223.   PutMsg (&ChildProcess->pr_MsgPort, (struct Message *) &PipeToUse->pfd_Msg);
  224.  
  225.   /* This is the normal exit point for the function. */
  226.   return ParentPipe;
  227.  
  228.   /* This code is only executed if there was an error. In this case the
  229.    * allocated resources must be freed. The code is actually clearer (at
  230.    * least in my opinion) and more concise by using goto than by using a
  231.    * function (global variables or function parameters needed) or a lot
  232.    * of if-constructions (code gets blown up unnecessarily).
  233.    */
  234. cleanup:
  235.   if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL)
  236.     DeleteMsgPort (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort);
  237.   if (ParentPipe)
  238.     fclose (ParentPipe);
  239.   if (ChildPipe)
  240.     Close (ChildPipe);
  241.   return NULL;
  242. }
  243.  
  244.  
  245. int pclose (stream)
  246. FILE *stream;
  247. {
  248.   LONG PipeToClose;
  249.  
  250.   /* Test whether we're using the right Dos version. */
  251.   if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
  252.     errno = EPIPE;
  253.     return -1;
  254.   }
  255.  
  256.   /* Test whether this is the first call to this module or not. If so,
  257.    * pclose has been called before popen and we return with an error
  258.    * because the initialisation has yet to be done.
  259.    */
  260.   if (FirstCall) {
  261.     errno = EBADF;
  262.     return -1;
  263.   }
  264.  
  265.   /* Search for the correct table entry and close the associated file. */
  266.   for (PipeToClose = 0; PipeToClose < MAX_OPEN_PIPES; PipeToClose++)
  267.     if (OpenPipes[PipeToClose].pfd_File == stream) break;
  268.   if (PipeToClose >= MAX_OPEN_PIPES) {
  269.     errno = EBADF;
  270.     return -1;
  271.   }
  272.   fclose (stream);
  273.  
  274.   /* Now wait for the child to terminate and get its exit status. */
  275.   WaitPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
  276.   OpenPipes[PipeToClose].pfd_File = NULL;
  277.  
  278.   /* Free the allocates resources. */
  279.   DeleteMsgPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
  280.   free (OpenPipes[PipeToClose].pfd_Msg.sm_Cmd);
  281.  
  282.   return OpenPipes[PipeToClose].pfd_Msg.sm_RetCode;
  283. }
  284.  
  285.  
  286. static void CleanUpPipes ()
  287. {
  288.   LONG Count;
  289.   FILE *Pipe;
  290.  
  291.   /* Close each open pipe. */
  292.   for (Count = 0; Count < MAX_OPEN_PIPES; Count++) {
  293.     Pipe = OpenPipes[Count].pfd_File;
  294.     if (Pipe != NULL)
  295.       pclose (Pipe);
  296.   }
  297. }
  298.  
  299.  
  300. static int __saveds ChildEntry ()
  301. {
  302.   struct Process *ChildProc;
  303.   struct StartupMessage *StartUpMessage;
  304.   LONG ReturnCode;
  305.   struct DosLibrary *DOSBase;
  306.   struct TagItem SysTags[3] = {
  307.     {SYS_Asynch, FALSE},
  308.     {SYS_UserShell, TRUE},
  309.     {TAG_DONE, 0}
  310.   };
  311.  
  312.   /* We need to open this library, because we don't inherit it from our
  313.    * parent process.
  314.    */
  315.   DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", DOS_VERSION);
  316.  
  317.   /* Get the childs process structure. */
  318.   ChildProc = (struct Process *) FindTask (NULL);
  319.  
  320.   /* Wait for the startup message from the parent. */
  321.   WaitPort (&ChildProc->pr_MsgPort);
  322.   StartUpMessage = (struct StartupMessage *) GetMsg (&ChildProc->pr_MsgPort);
  323.  
  324.   /* Now run the command and return the result. */
  325.   if (DOSBase != NULL)
  326.     ReturnCode = System (StartUpMessage->sm_Cmd, SysTags);
  327.   else
  328.     ReturnCode = 10000;
  329.   StartUpMessage->sm_RetCode = ReturnCode;
  330.  
  331.   /* Tell the parent that we are done. */
  332.   ReplyMsg ((struct Message *) StartUpMessage);
  333.  
  334.   if (DOSBase)
  335.     CloseLibrary ((struct Library *) DOSBase);
  336.  
  337.   return 0;
  338. }
  339.  
  340. #endif /* PIPES */
  341.